﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Helper;

public static class FontSplitter {
    public const string DEFAULT_IMAGE_SPLIT_STRING = "!\"%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz’¡¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖòóôöØÙÚÛÜùúûüÝÞßàáâãäåæçèéêëìíîïøĄąĆćĘęŁłŃńŹźñ";
    public const string LETTERS_UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public const string LETTERS_LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
    public const string LETTERS_SPECIAL = "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖòóôöØÙÚÛÜùúûüÝÞßàáâãäåæçèéêëìíîïøĄąĆćĘęŁłŃńŹźñ";
    public const string NUMBERS = "0123456789";
    public const string SYMBOLS = "½§!\"@#£¤$%&\\/()<>{}[]=?+`´|^¨~'*,;.:-_’¡¿";
    public const string ALL_CHARACTERS = NUMBERS + LETTERS_UPPERCASE + LETTERS_LOWERCASE + LETTERS_SPECIAL + SYMBOLS;

    public const string FILENAME_FORMAT = "0x{0:x4}.png";

    private static bool ClearColumn(Bitmap b, int x) {
        if(x >= b.Width || x < 0) {
            return true;
        }
        for(int y = 0; y < b.Height; y++) {
            if(b.GetPixel(x, y).A != 0) {
                return false;
            }
        }
        return true;
    }

    private static bool ClearRow(Bitmap b, int y) {
        if(y >= b.Height || y < 0) {
            return false;
        }
        for(int x = 0; x < b.Width; x++) {
            if(b.GetPixel(x, y).A != 0) {
                return false;
            }
        }
        return true;
    }

    public static void SplitImage(string directoryPath, string imagePath, string splitString, bool proportional) {
        Profiler.Start("Total Time: Process image", true);

        if(File.Exists(imagePath)) {
            ProgressForm progressForm = new ProgressForm();
            progressForm.MaxValue(splitString.Length);
            progressForm.Show();

            Bitmap fontBmp = Bitmap.FromFile(imagePath) as Bitmap;

            int width = 0;
            int height = 0;
            int xoff = 0;
            int yoff = 0;

            while(ClearRow(fontBmp, yoff)) {
                yoff++;
            }

            height = fontBmp.Height - yoff;
            while(ClearRow(fontBmp, yoff + height - 1)) {
                height--;
            }

            // find first char
            if(proportional) {
                while(ClearColumn(fontBmp, xoff)) {
                    xoff++;
                }
            }

            for(int i = 0; i < splitString.Length && xoff < fontBmp.Width; i++) {
                if(proportional) {
                    xoff += width;
                    while(ClearColumn(fontBmp, xoff) && xoff < fontBmp.Width) {
                        xoff++;
                    }
                    width = 1;
                    while(!ClearColumn(fontBmp, xoff + width)) {
                        width++;
                    }
                } else {
                    xoff = i * width;
                    width = fontBmp.Width / splitString.Length;
                }

                if(xoff >= fontBmp.Width) {
                    break;
                }

                string fileName = string.Format(FILENAME_FORMAT, (int)splitString[i]);
                string filePath = Path.Combine(directoryPath, fileName);

                using(Bitmap newBmp = new Bitmap(width, height)) {
                    Graphics g = Graphics.FromImage(newBmp);
                    g.DrawImage(fontBmp, new Rectangle(0, 0, width, height), xoff, yoff, width, height, GraphicsUnit.Pixel);

                    newBmp.Save(filePath);
                    progressForm.Step();
                }
            }

            progressForm.Dispose();
        }

        Profiler.Stop("Total Time: Process image");
        Console.WriteLine(Profiler.OutputString());
    }

    public static void SplitFont(string directoryPath, Font font, Color color, string splitString) {
        Profiler.Start("Total Time: Process font", true);

        ProgressForm progressForm = new ProgressForm();
        progressForm.MaxValue(splitString.Length);
        progressForm.Show();

        using(Graphics dummyGraphics = Graphics.FromHwnd(new IntPtr())) {
            Brush brush = new SolidBrush(color);
            SizeF fontSize;
            Graphics g = null;
            Bitmap charBmp = null;

            for(int i = 0; i < splitString.Length; i++) {
                char c = splitString[i];
                string fileName = string.Format(FILENAME_FORMAT, (int)c);
                string filePath = Path.Combine(directoryPath, fileName);
                string character = c.ToString();

                fontSize = dummyGraphics.MeasureString(character, font);

                charBmp = new Bitmap((int)fontSize.Width, (int)fontSize.Height);
                g = Graphics.FromImage(charBmp);
                g.DrawString(character, font, brush, 0, 0);

                charBmp = Crop(ref charBmp);
                charBmp.Save(filePath);
                progressForm.Step();
            }

            brush.Dispose();
            charBmp.Dispose();
            g.Dispose();
            progressForm.Dispose();
        }

        Profiler.Stop("Total Time: Process font");
        Console.WriteLine(Profiler.OutputString());
    }

    private static Bitmap Crop(ref Bitmap bmp) {
        Vector4 vec = CropRegion(ref bmp);
        Bitmap croppedBmp = new Bitmap(vec.Width, vec.Height);

        using(Graphics g = Graphics.FromImage(croppedBmp)) {
            g.DrawImage(bmp, -vec.minX, -vec.minY);
        }

        return croppedBmp;
    }

    private static Vector4 CropRegion(ref Bitmap bmp) {
        int width = bmp.Width;
        int height = bmp.Height;
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        int minX = int.MaxValue;
        int maxX = int.MinValue;
        int minY = 0;
        int maxY = int.MinValue;

        unsafe {
            byte* bmpPointer = (byte*)bmpData.Scan0;
            int offset = bmpData.Stride - (bmpData.Width << 2);

            for(int y = 0; y < height; y++) {
                for(int x = 0; x < width; x++) {
                    if(bmpPointer[3] != 0) {
                        if(x < minX) {
                            minX = x;
                        }
                        if(x > maxX) {
                            maxX = x;
                        }
                        //if(y < minY) {
                        //    minY = y;
                        //}
                        if(y > maxY) {
                            maxY = y;
                        }
                    }
                    bmpPointer += 4;
                }
                bmpPointer += offset;
            }
        }

        bmp.UnlockBits(bmpData);
        return new Vector4(minX, minY, maxX, maxY);
    }

    private struct Vector4 {
        public int minX;
        public int minY;
        public int maxX;
        public int maxY;

        public Vector4(int minX, int minY, int maxX, int maxY) {
            this.minX = minX;
            this.minY = minY;
            this.maxX = maxX;
            this.maxY = maxY;
        }

        public int Width {
            get { return maxX - minX + 1; }
        }

        public int Height {
            get { return maxY - minY + 1; }
        }
    }
}